// The contents of this file are subject to the Mozilla Public License Version
// 1.1
//(the "License"); you may not use this file except in compliance with the
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo
// Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.
package org.columba.mail.gui.config.subscribe;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

import org.columba.api.command.IWorkerStatusController;
import org.columba.core.base.ListTools;
import org.columba.core.command.Command;
import org.columba.mail.folder.imap.IMAPRootFolder;
import org.columba.mail.imap.IImapServer;
import org.columba.ristretto.imap.ListInfo;
import org.columba.ristretto.imap.Namespace;
import org.columba.ristretto.imap.NamespaceCollection;
import org.frapuccino.checkabletree.CheckableItemImpl;

public class SynchronizeFolderListCommand extends Command {

	private IMAPRootFolder root;

	private IImapServer store;

	private TreeNode node;

	/**
	 * @param references
	 */
	public SynchronizeFolderListCommand(SubscribeCommandReference reference) {
		super(reference);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.columba.api.command.Command#execute(org.columba.api.command.Worker)
	 */
	public void execute(IWorkerStatusController worker) throws Exception {
		root = (IMAPRootFolder) ((SubscribeCommandReference) getReference())
				.getSourceFolder();

		store = root.getServer();

		node = createTreeStructure();
	}

	private List fetchUnsubscribedFolders(String reference) throws Exception {
		NamespaceCollection namespaces;

		// Does the server support the namespace extension?
		if (store.isSupported("NAMESPACE")) {
			namespaces = store.fetchNamespaces();
		} else {
			// create default namespace
			namespaces = new NamespaceCollection();
			namespaces.addPersonalNamespace(new Namespace("", "/"));
		}

		ArrayList result = new ArrayList();
		Iterator it;

		// Process personal namespaces
		if (namespaces.getPersonalNamespaceSize() > 0) {
			it = namespaces.getPersonalIterator();
			while (it.hasNext()) {
				Namespace pN = (Namespace) it.next();

				ListInfo[] list = store.list("", pN.getPrefix() + '%');
				result.addAll(Arrays.asList(list));
			}
		}

		// Process other users namespaces
		if (namespaces.getOtherUserNamespaceSize() > 0) {
			it = namespaces.getOtherUserIterator();
			while (it.hasNext()) {
				Namespace pN = (Namespace) it.next();

				ListInfo[] list = store.list("", pN.getPrefix() + '%');
				result.addAll(Arrays.asList(list));
			}
		}

		// Process shared namespaces
		if (namespaces.getSharedNamespaceSize() > 0) {
			it = namespaces.getSharedIterator();
			while (it.hasNext()) {
				Namespace pN = (Namespace) it.next();

				ListInfo[] list = store.list("", pN.getPrefix() + '%');
				result.addAll(Arrays.asList(list));
			}
		}

		for(int i=0; i<result.size(); i++ ) {
			ListInfo info = (ListInfo) result.get(i);
			// Handle special case in which INBOX has a NIL delimiter
			// -> there might exist a pseudo hierarchy under INBOX+delimiter
			if (info.getName().equalsIgnoreCase("INBOX") && info.getDelimiter() == null) {
					result.addAll(Arrays.asList(store.list("", "INBOX"
							+ store.getDelimiter() + '%')));
				break;
			}
			
			// If this folder has children add them
			// TODO: In the future we should try to fetch additional children on demand
			// when the tree of the dialog is opened
			if( info.getParameter(ListInfo.HASCHILDREN)) {
				result.addAll(Arrays.asList(store.list("", info.getName()
						+ store.getDelimiter() + '%')));				
			}
		}

		return result;
	}

	private TreeNode createTreeStructure() throws Exception {
		ListInfo[] lsub = store.fetchSubscribedFolders();

		// Create list of unsubscribed folders
		List subscribedFolders = new ArrayList(Arrays.asList(lsub));
		// INBOX is always subscribed
		subscribedFolders.add(new ListInfo("INBOX", null, 0));

		List unsubscribedFolders = fetchUnsubscribedFolders("");
		ListTools.substract(unsubscribedFolders, subscribedFolders);

		// Now we have the subscribed folders in subscribedFolders
		// and the unsubscribed folders in unsubscribedFolders
		// Next step: Create a treestructure
		CheckableItemImpl rootNode = new CheckableItemImpl(root.getName());

		Iterator it = unsubscribedFolders.iterator();

		while (it.hasNext()) {
			ListInfoTreeNode node = insertTreeNode((ListInfo) it.next(),
					rootNode);
			node.setSelected(false);
		}

		it = subscribedFolders.iterator();

		while (it.hasNext()) {
			ListInfoTreeNode node = insertTreeNode((ListInfo) it.next(),
					rootNode);
			node.setSelected(true);
		}
		return rootNode;
	}

	private ListInfoTreeNode insertTreeNode(ListInfo listInfo,
			DefaultMutableTreeNode parent) {
		// split the hierarchical name with at the delimiters
		String[] hierarchy = listInfo.getName().split(
				"\\" + listInfo.getDelimiter());

		DefaultMutableTreeNode actParent = parent;
		StringBuffer mailboxName = new StringBuffer();

		mailboxName.append(hierarchy[0]);
		actParent = ensureChild(hierarchy[0], mailboxName.toString(), actParent);

		for (int i = 1; i < hierarchy.length; i++) {
			mailboxName.append(listInfo.getDelimiter());
			mailboxName.append(hierarchy[i]);
			actParent = ensureChild(hierarchy[i], mailboxName.toString(),
					actParent);
		}

		return (ListInfoTreeNode) actParent;
	}

	private DefaultMutableTreeNode ensureChild(String name, String mailbox,
			DefaultMutableTreeNode parent) {
		Enumeration children = parent.children();
		ListInfoTreeNode node;

		while (children.hasMoreElements()) {
			node = (ListInfoTreeNode) children.nextElement();

			if (node.toString().equals(name)) {
				return node;
			}
		}

		node = new ListInfoTreeNode(name, mailbox);
		parent.add(node);

		return node;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.columba.api.command.Command#updateGUI()
	 */
	public void updateGUI() throws Exception {
		SubscribeDialog dialog = ((SubscribeCommandReference) getReference())
				.getDialog();

		dialog.syncFolderListDone(new DefaultTreeModel(node));
	}
}